Skip to content

fix: tolerate non-conforming apps across all app read paths (#48) - #85

Open
kstlouis wants to merge 5 commits into
okta:mainfrom
kstlouis:fix/resilient-app-parsing
Open

fix: tolerate non-conforming apps across all app read paths (#48)#85
kstlouis wants to merge 5 commits into
okta:mainfrom
kstlouis:fix/resilient-app-parsing

Conversation

@kstlouis

Copy link
Copy Markdown

Summary

Fixes #48 across all three application read paths. The Okta Python SDK bulk-deserializes app records into strict pydantic models, so a single non-conforming record aborts the entire response. This affects real orgs constantly: App Catalog SAML apps with a partial settings.signOn, and custom SWA apps whose name falls outside the template_swa/template_swa3field enum (e.g. appstoreconnect, dockerhub).

The fix fetches the raw record(s) through the SDK request executor and parses each one individually, falling back to the raw dict (tagged with a _deserialization_warning marker) when strict deserialization fails. Conforming apps are still returned as typed models; pagination is unchanged.

What's covered

Path Tool Status
GET /api/v1/apps list_applications from #64 (credit @mjdavidson)
GET /api/v1/groups/{id}/apps list_group_apps new here
GET /api/v1/apps/{id} get_application new here

#64 fixed list_applications but #48 also reports get_application and the group-apps listing failing. This PR incorporates #64 (its commit is preserved with original authorship) and extends the same _safe_parse_app approach to the other two paths so #48 is fully closed. If #64 is merged first, the first commit here drops out cleanly on rebase.

Notes

  • list_group_apps and get_application previously went through the typed client.list_assigned_applications_for_group / client.get_application, which is where the strict validation happened — both now use the request-executor + per-record parse, mirroring list_applications.
  • The _safe_parse_app / _camel_case_param helpers from fix: tolerate non-conforming apps when listing applications #64 are reused as-is.

Tests

  • tests/test_list_applications.py (from fix: tolerate non-conforming apps when listing applications #64)
  • tests/test_pagination.py::TestListGroupAppsFetchAll updated to the executor path + a new "one bad SAML record doesn't abort the listing" case
  • tests/test_get_application.py (new) — good record → typed model, non-conforming → raw dict with warning, executor error surfaced, expand passthrough

Full suite: 412 passed.

mjdavidson and others added 3 commits June 29, 2026 11:21
list_applications deserialized the whole page into strict SDK models in a
single pass, so one record the models reject — a SAML app with a partial
settings.signOn, or a provisioning features value outside the SDK enum —
raised a validation error that aborted the entire response. A single
non-conforming app made the tool unusable on real orgs.

Fetch the apps page through the SDK request executor and parse each record
individually, falling back to the raw dict (tagged with a warning) when strict
deserialization fails. Conforming apps are still returned as typed models, and
pagination is unchanged.
Extends the resilient per-record parsing from PR okta#64 (list_applications) to
list_group_apps, which went through client.list_assigned_applications_for_group
and hit the same failure: the SDK bulk-deserializes the whole page into strict
pydantic models, so one App Catalog SAML app (sparse settings.signOn) or custom
SWA app (name outside the template enum) aborts the entire response.

Now fetches /api/v1/groups/{id}/apps through the request executor and parses
each record via _safe_parse_app, falling back to the raw dict with a warning
marker. Both the initial fetch and the fetch_all pagination path are covered.

Updates the TestListGroupAppsFetchAll suite to mock the executor path and adds
a resilience test (one bad SAML record no longer aborts the listing).

Refs okta#48
Completes the okta#48 fix. get_application went through the typed client.get_application,
which validates the record into a strict SDK model — so an App Catalog SAML app with
a partial settings.signOn (or a custom SWA whose name is outside the template enum)
raised and the call failed outright, as reported in okta#48.

Fetches the record via the request executor (GET /api/v1/apps/{id}) and parses it
through _safe_parse_app, returning the raw dict with a warning marker when strict
deserialization fails — same approach as list_applications and list_group_apps.

Refs okta#48
…utor

The resilient list_applications/list_group_apps path fetches pages through the
request executor, which returns the raw aiohttp response. extract_after_cursor
only understood the typed-SDK shapes (ApiResponse.headers / OktaAPIResponse._next),
so on the executor response it found no cursor and pagination stopped after the
first page — listings silently truncated to one page (e.g. fetch_all returned only
20 items) on accounts/groups with more.

Root cause: Okta returns 'self' and 'next' as SEPARATE Link headers, so a multidict
headers.get('Link') yields only 'self'. aiohttp already parses them into .links
(rel-keyed), which is what the SDK itself uses (OktaAPIResponse.extract_pagination).

extract_after_cursor now reads response.links['next'] first (raw executor response),
then falls back to a Link-header string via .headers / get_headers() / _resp_headers
(ApiResponse and other shapes), then the SDK v2 has_next()/_next path. Adds tests for
the aiohttp .links shape and the header-accessor fallbacks.

Refs okta#48
@kstlouis
kstlouis force-pushed the fix/resilient-app-parsing branch from 095978c to 128b6ba Compare June 29, 2026 22:24
@BinoyOza-okta
BinoyOza-okta self-requested a review July 2, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pydantic validation errors block listing/retrieving Okta App Catalog SAML apps and Atlassian SWA apps; single bad record poisons entire response page

2 participants